﻿using System;
using System.Collections.Generic;

namespace MFramework
{
    /*
     * 基于多播委托是一种不太好的实现，mono下MulticastDelegate增删都会重新创建delegate数组，已替换为List
     * 禁止在注册的监听函数中再注册/反注册，可能导致在广播的过程中遍历出错
     */
    public class EventDispatcher
    {
        public delegate void CallBack();
        public delegate void CallBack<T>(T arg1);
        public delegate void CallBack<T, X>(T arg1, X arg2);
        public delegate void CallBack<T, X, Y>(T arg1, X arg2, Y arg3);
        public delegate void CallBack<T, X, Y, Z>(T arg1, X arg2, Y arg3, Z arg4);

        private static Dictionary<EventDefine, List<Delegate>> m_EventDic = new Dictionary<EventDefine, List<Delegate>>();
        private static int callStackDeep = 0;

        #region Regist
        public static void Regist(EventDefine eventCode, CallBack callBack)
        {
            _Regist(eventCode, callBack);
        }

        public static void Regist<T>(EventDefine eventCode, CallBack<T> callBack)
        {
            _Regist(eventCode, callBack);
        }

        public static void Regist<T, X>(EventDefine eventCode, CallBack<T, X> callBack)
        {
            _Regist(eventCode, callBack);
        }

        public static void Regist<T, X, Y>(EventDefine eventCode, CallBack<T, X, Y> callBack)
        {
            _Regist(eventCode, callBack);
        }

        public static void Regist<T, X, Y, Z>(EventDefine eventCode, CallBack<T, X, Y, Z> callBack)
        {
            _Regist(eventCode, callBack);
        }

        private static void _Regist(EventDefine eventCode, Delegate callBack)
        {
            OnPreRegist(eventCode, callBack);
            m_EventDic[eventCode].Add(callBack);
        }
        #endregion

        #region UnRegist
        public static void UnRegist(EventDefine eventCode, CallBack callBack)
        {
            _UnRegist(eventCode, callBack);
        }

        public static void UnRegist<T>(EventDefine eventCode, CallBack<T> callBack)
        {
            _UnRegist(eventCode, callBack);
        }

        public static void UnRegist<T, X>(EventDefine eventCode, CallBack<T, X> callBack)
        {
            _UnRegist(eventCode, callBack);
        }

        public static void UnRegist<T, X, Y>(EventDefine eventCode, CallBack<T, X, Y> callBack)
        {
            _UnRegist(eventCode, callBack);
        }

        public static void UnRegist<T, X, Y, Z>(EventDefine eventCode, CallBack<T, X, Y, Z> callBack)
        {
            _UnRegist(eventCode, callBack);
        }

        private static void _UnRegist(EventDefine eventCode, Delegate callBack)
        {
            OnPreUnRegist(eventCode, callBack);

            bool success = m_EventDic[eventCode].Remove(callBack);

            if (!success)
            {
                throw new Exception(string.Format("反注册异常：尝试为事件{0}反注册一个不存在的监听", eventCode));
            }
        }
        #endregion

        #region Dispatch
        public static void Dispatch(EventDefine eventCode)
        {
            OnPreDispatch();
            if (m_EventDic.TryGetValue(eventCode, out List<Delegate> list))
            {
                if (list.Count > 0 && list[0] is CallBack)
                {
                    //用迭代器遍历，在出现违规操作破坏迭代器时及时抛出异常
                    foreach(CallBack callback in list)
                    {
                        callback?.Invoke();
                    }
                }
                else
                {
                    throw new Exception(string.Format("分发事件异常：事件{0}委托类型不匹配", eventCode));
                }
            }
            OnPostDispatch();
        }

        public static void Dispatch<T>(EventDefine eventCode, T arg)
        {
            OnPreDispatch();
            if (m_EventDic.TryGetValue(eventCode, out List<Delegate> list))
            {
                if (list.Count > 0 && list[0] is CallBack<T>)
                {
                    //用迭代器遍历，在出现违规操作破坏迭代器时及时抛出异常
                    foreach (CallBack<T> callback in list)
                    {
                        callback?.Invoke(arg);
                    }
                }
                else
                {
                    throw new Exception(string.Format("分发事件异常：事件{0}委托类型不匹配", eventCode));
                }
            }
            OnPostDispatch();
        }

        public static void Dispatch<T, X>(EventDefine eventCode, T arg1, X arg2)
        {
            OnPreDispatch();
            if (m_EventDic.TryGetValue(eventCode, out List<Delegate> list))
            {
                if (list.Count > 0 && list[0] is CallBack<T, X>)
                {
                    //用迭代器遍历，在出现违规操作破坏迭代器时及时抛出异常
                    foreach (CallBack<T, X> callback in list)
                    {
                        callback?.Invoke(arg1, arg2);
                    }
                }
                else
                {
                    throw new Exception(string.Format("分发事件异常：事件{0}委托类型不匹配", eventCode));
                }
            }
            OnPostDispatch();
        }

        public static void Dispatch<T, X, Y>(EventDefine eventCode, T arg1, X arg2, Y arg3)
        {
            OnPreDispatch();
            if (m_EventDic.TryGetValue(eventCode, out List<Delegate> list))
            {
                if (list.Count > 0 && list[0] is CallBack<T, X, Y>)
                {
                    //用迭代器遍历，在出现违规操作破坏迭代器时及时抛出异常
                    foreach (CallBack<T, X, Y> callback in list)
                    {
                        callback?.Invoke(arg1, arg2, arg3);
                    }
                }
                else
                {
                    throw new Exception(string.Format("分发事件异常：事件{0}委托类型不匹配", eventCode));
                }
            }
            OnPostDispatch();
        }

        public static void Dispatch<T, X, Y, Z>(EventDefine eventCode, T arg1, X arg2, Y arg3, Z arg4)
        {
            OnPreDispatch();
            if (m_EventDic.TryGetValue(eventCode, out List<Delegate> list))
            {
                if (list.Count > 0 && list[0] is CallBack<T, X, Y, Z>)
                {
                    //用迭代器遍历，在出现违规操作破坏迭代器时及时抛出异常
                    foreach (CallBack<T, X, Y, Z> callback in list)
                    {
                        callback?.Invoke(arg1, arg2, arg3, arg4);
                    }
                }
                else
                {
                    throw new Exception(string.Format("分发事件异常：事件{0}委托类型不匹配", eventCode));
                }
            }
            OnPostDispatch();
        }
        #endregion

        private static void OnPreRegist(EventDefine eventCode, Delegate callBack)
        {
            if (m_EventDic.ContainsKey(eventCode) && m_EventDic[eventCode].Count > 0)
            {
                Delegate d = m_EventDic[eventCode][0];
                if (d.GetType() != callBack.GetType())
                {
                    throw new Exception(string.Format("尝试为事件{0}添加不同类型的委托，当前委托{1}，预注册{2}", eventCode, d.GetType(), callBack.GetType()));
                }
            }
            else
            {
                m_EventDic.Add(eventCode, new List<Delegate>());
            }
        }
        private static void OnPreUnRegist(EventDefine eventCode, Delegate callBack)
        {
            if (m_EventDic.ContainsKey(eventCode) && m_EventDic[eventCode].Count > 0)
            {
                Delegate d = m_EventDic[eventCode][0];
                if (d.GetType() != callBack.GetType())
                {
                    throw new Exception(string.Format("反注册异常：尝试为事件{0}移除不同类型的委托，当前委托{1}，预移除委托{2}", eventCode, d.GetType(), callBack.GetType()));
                }
            }
            else
            {
                throw new Exception(string.Format("反注册异常：事件{0}不存在对应委托监听", eventCode));
            }
        }

        private static void OnPreDispatch()
        {
            ++callStackDeep;

            if (callStackDeep > 10)
            {
                throw new Exception("可能存在事件循环！事件触发调用栈深度大于10！");
            }
        }

        private static void OnPostDispatch()
        {
            --callStackDeep;
        }
    }
}